package com.lotus.vigil.service.processor;

import com.lotus.vigil.model.*;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;

public class FaultDataProcessor {
    /**
     * 处理故障数据
     * @param records 故障记录列表
     * @param endTime 结束时间
     * @return 设备故障统计列表
     */
    public static List<DeviceFaultStats> process(List<DeviceFaultRecords> records, LocalDateTime endTime){

        // 1. 过滤出有效的故障事件 : 按照故障ID聚合事件
        Map<String, FaultEventGroup> eventGroups = groupEvents(records);

        // 2. 计算每个故障事件的统计数据: 转换为时间指标
        List<FaultMetrics> metrics = calculateMetrics(eventGroups,endTime);

        return aggregateDeviceStats(metrics);
    }


    // 聚合设备统计数据
    private static List<DeviceFaultStats> aggregateDeviceStats(List<FaultMetrics> metrics) {
        // 1. 按照设备ID和故障类型分组
        Map<Long, Map<Integer, List<FaultMetrics>>> grouped = metrics.stream()
                .collect( Collectors.groupingBy(
                        FaultMetrics::getDeviceId,
                        Collectors.groupingBy(FaultMetrics::getFaultType)
                ));

        return grouped.entrySet().stream()
                .map(entry -> {
                    Long deviceId = entry.getKey();
                    DeviceFaultStats stats = new DeviceFaultStats();
                    stats.setDeviceId(deviceId);

                    entry.getValue().forEach((faultType,typeMetrics) -> {
                        // 硬件故障处理
                        if(faultType == 1){
                            stats.setHdAvgRetention(calculateAvg(typeMetrics, m -> m.getRetentionMillis()));
                            stats.setHdMaxRetention(calculateMax(typeMetrics, m -> m.getRetentionMillis()));
                            stats.setHdAvgFix(calculateAvg(
                                            typeMetrics.stream().filter(m -> m.getFixMillis() != null)
                                                    .collect(Collectors.toList()),
                                            m -> m.getFixMillis()
                                    )
                            );
                            stats.setHdMaxFault(calculateMax(typeMetrics, m -> m.getFaultMillis()));
                        }
                        // 软件故障处理
                        else if (faultType == 2){
                            stats.setSfAvgRetention(calculateAvg(typeMetrics, m -> m.getRetentionMillis()));
                            stats.setSfMaxRetention(calculateMax(typeMetrics, m -> m.getRetentionMillis()));
                            stats.setSfAvgFix(calculateAvg(
                                            typeMetrics.stream().filter(m -> m.getFixMillis() != null)
                                                    .collect(Collectors.toList()),
                                            m -> m.getFixMillis()
                                    )
                            );
                            stats.setSfMaxFault(calculateMax(typeMetrics, m -> m.getFaultMillis()));
                        }
                    });

                    return stats;
                }).sorted(Comparator.comparingLong(DeviceFaultStats::getDeviceId)).collect(Collectors.toList());
    }


    // 计算指标
    private static List<FaultMetrics> calculateMetrics(Map<String, FaultEventGroup> eventGroups, LocalDateTime endTime) {
        return eventGroups.values().stream()
                .filter(FaultEventGroup::isValidGroup)
                .map(group -> {
                    FaultMetrics metrics = new FaultMetrics();
                    metrics.setDeviceId(group.getDeviceId());
                    metrics.setFaultType(group.getFaultType());

                    // 计算滞留时间
                    LocalDateTime dispatchTime = group.getDispatchTime();

                    metrics.setRetentionMillis(
                            Duration.between(
                                    group.getCreateTime(),
                                    dispatchTime != null ? dispatchTime : endTime
                            ).toMillis()
                    );

                    // 计算修复时间
                    if (dispatchTime != null && group.getFixTime() != null) {
                        metrics.setFixMillis(
                                Duration.between(
                                        dispatchTime,
                                        group.getFixTime()
                                ).toMillis()
                        );
                    }

                    // 计算总故障时间
                    LocalDateTime fixTime = group.getFixTime();
                    metrics.setFaultMillis(
                            Duration.between(
                                    group.getCreateTime(),
                                    fixTime != null ? fixTime : endTime
                            ).toMillis()
                    );

                    return metrics;

                })
                .collect(Collectors.toList());
    }

    // 聚合事件
    private static Map<String, FaultEventGroup> groupEvents(List<DeviceFaultRecords> records) {
        return records.stream().collect(Collectors.groupingBy(
                DeviceFaultRecords::getFaultId,
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> {
                            FaultEventGroup faultEventGroup = new FaultEventGroup();
                            list.forEach(record -> {
                                switch (record.getOpType()) {
                                    case 1:
                                        faultEventGroup.setFaultId(record.getFaultId());
                                        faultEventGroup.setDeviceId(record.getDeviceId());
                                        faultEventGroup.setFaultType(record.getFaultType());
                                        faultEventGroup.setCreateTime(record.getOccurTime());
                                        break;
                                    case 2:
                                        faultEventGroup.setDispatchTime(record.getOccurTime());
                                        break;
                                    case 3:
                                        faultEventGroup.setFixTime(record.getOccurTime());
                                        break;
                                    default:
                                        break;
                                }
                            });
                            return faultEventGroup;
                        }
                )
        ));
    }


    // 工具方法：计算平均值（向下取整）
    private static Long calculateAvg(List<FaultMetrics> metrics, ToLongFunction<FaultMetrics> mapper) {
        return (long) Math.floor(metrics.stream()
                .mapToLong(mapper)
                .average()
                .orElse(0.0));
    }

    // 工具方法：计算最大值
    private static Long calculateMax(List<FaultMetrics> metrics, ToLongFunction<FaultMetrics> mapper) {
        return metrics.stream()
                .mapToLong(mapper)
                .max()
                .orElse(0L);
    }


}
